;;;; game-api.lisp

(in-package #:gamao-impl)

#|
The following parameters represent the signatures of game api generic functions.
|#

(eval-when (:compile-toplevel :load-toplevel :execute)

  (defparameter *game-actions* '( ;; rules
                                  (new-dice game die1 die2)
                                  (roll-dice game player)
                                  (pick-up-dice game player)
                                  (offer-double game player)
                                  (accept-double game player)
                                  (reject-double game player)
                                  ;; checker-game
                                  (move-checker game player from pips)
                                  (undo-move game player)
                                  ;; automatic actions
                                  (set-dice game die1 die2)))

  ;; TODO: signature may-roll-dice-p (game)
  (defparameter *game-legality-checks* '((may-set-dice-p new-dice)
                                         (may-roll-dice-p roll-dice)
                                         (may-move-checker-p move-checker)
                                         (may-pick-up-dice-p pick-up-dice)
                                         (may-undo-move-p undo-move)
                                         (may-offer-double-p offer-double)
                                         (may-accept-reject-double-p accept-double reject-double)))

  ;; These are setfable on the low level only.
  (defparameter *game-queries* '(;; board
                                 (die1 game)
                                 (die2 game)
                                 (current-player game)
                                 (game-result game)
                                 (dice-rolling-p game)
                                 (cube-offered-p game)
                                 (cube-value game)
                                 (cube-owner game)
                                 (checker-position game)
                                 ;; checker-game
                                 (remaining-dice-count game)
                                 (remaining-die game n)
                                 ;; rules
                                 (game-score game)
                                 (winner game)
                                 ))

  (defparameter *game-docstrings*
    '((new-dice "Set the dice of the game.")
      (roll-dice "The player opts to roll dice rather than offer a double.")
      (pick-up-dice "The player finalizes a full checker move.")
      (offer-double "The player offers double.")
      (accept-double "The player accepts the double.")
      (reject-double "The player rejects the double.")
      (move-checker "The player makes a partial checker move.")
      (undo-move "The player undoes the last partial checker move.")
      (may-set-dice-p "Check whether dice may be set.")
      (may-roll-dice-p "Check whether the player can opt to roll dice.")
      (may-move-checker-p "Check whether the player may make the partial checker move.")
      (may-pick-up-dice-p "Check whether the player may complete the full checker move.")
      (may-undo-move-p "Check whether the player may undo a partial checker move.")
      (may-offer-double-p "Check whether the player may offer double.")
      (may-accept-reject-double-p "Check whether the player may decide on the offered cube.")
      (die1 "The first die of the game; NIL if unavailable.")
      (die2 "The second die of the game; NIL if unavailable.")
      (current-player "NIL before the first turn was decided; :BLACK or :WHITE while the game is played.")
      (game-result "NIL while the game is played; one of :SINGLE, :GAMMON, :BACKGAMMON, or :DROPPED-DOUBLE after it finished.")
      (dice-rolling-p game "Return true if the dice was rolled and not set yet, NIL otherwise.")
      (cube-offered-p "Return true if the cube is offered, NIL otherwise")
      (cube-value "NIL if the cube is unavailable and the value of the cube otherwise.")
      (cube-owner "Return the player who owns the cube or NIL no one does.")
      (checker-position "Current CHECKER-POSITION.")
      (remaining-dice-count "The number of dice remaining to be played.")
      (remaining-die "n-th die remaining to be played.")
      (game-score "The number of points won for this game when the game is over.")
      (winner "The winner or NIL if the game is not over yet.")))

  (defun generic-definition-forms (definitions)
    `(progn
       ,@(loop for (function . arguments) in definitions
               for docstring = (second (find function *game-docstrings* :key #'first))
               collect `(defgeneric ,function ,arguments
                          ,@(if docstring (list `(:documentation ,docstring)) nil)))
       ',(mapcar #'first definitions)))

  ) ; eval-when

(defmacro define-game-actions ()
  (generic-definition-forms *game-actions*))

(defmacro define-game-queries ()
  (generic-definition-forms *game-queries*))

(defmacro define-game-legality-checks ()
  (let ((defs (loop for (check-name action-name . rest) in *game-legality-checks*
                    collect (substitute check-name action-name (assoc action-name *game-actions*)))))
    (generic-definition-forms defs)))

(define-game-actions)
(define-game-legality-checks)
(define-game-queries)

